package com.zx.deploy.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.zx.deploy.common.exception.BusinessException;
import com.zx.deploy.common.os.OSinfo;
import com.zx.deploy.common.utils.CommonUtil;
import com.zx.deploy.common.utils.DatetimeUtil;
import com.zx.deploy.common.utils.FileUtil;
import com.zx.deploy.common.utils.PackUtil;
import com.zx.deploy.common.utils.RuntimeUtil;
import com.zx.deploy.config.DeployConfig;
import com.zx.deploy.model.SpeedProgress;
import com.zx.deploy.service.DataBaseService;
import com.zx.deploy.service.SoftService;

/**
 * 程序部署实现
 * 
 * @author jbyu
 *
 */
@Service
public class SoftServiceImpl implements SoftService {
	

	/**
	 * 环境变量
	 */
	@Autowired
	protected DeployConfig config;
	/**
	 * 数据库操作impl EnvDataBaseService JdbcDataBaseService
	 */
	@Resource(name="${mysql.impl.className}")
	protected DataBaseService  dataBaseService;

	/**
	 * 解压包
	 */
	public void unPackage(DeployConfig config, SpeedProgress speed) {
        //清理临时文件区，如果因为之前发生错误未清理
		this.cleanTempFile(config, speed);
		String soft_file_path = config.soft_file_path.concat(config.soft_package_name);
		try {
			PackUtil.unZipFiles(new File (soft_file_path), config.soft_file_path);
			speed.setInfo("解压程序包完成");
		} catch (Exception e) {
			throw new BusinessException("解压缩失败");
			
		}
	}

	/**
	 * 执行脚本
	 */
	public void executeSql(DeployConfig config, SpeedProgress speed) {
		dataBaseService.executeSql(config, speed);
	}

	/**
	 * 备份包
	 */
	public void backupWar(DeployConfig config, List<String> wars,SpeedProgress speed) {
		try {
			String warpath = config.tomat_path.concat(config.tomcat_war_path)+File.separator;
			String backups_path = config.backups_war_path+DatetimeUtil.getCurrentDateTime("yyyyMMdd_HHmmss")+File.separator;
			File temp=new File(backups_path);
			if (!temp.exists())
				temp.mkdirs();
			for (String warName : wars) {
				if ("separate".equals(config.tomcat_model)) {
					String value=config.getWarStorageList().get(warName);
					warpath = config.tomat_path.concat(value).concat(File.separator);
				}
				speed.setInfo(String.format("正在备份%s",warName));
				File srcFile = new File(warpath.concat(warName));
				File destFile = new File(backups_path.concat(warName));
				if (srcFile.exists() && srcFile.canRead())
					FileUtils.copyFile(srcFile, destFile);
				speed.setInfo(String.format("%s备份完成",warName));
			}
		} catch (Exception e) {
			throw new BusinessException("备份war包失败");
		}
	}

	/**
	 * 停止服务
	 * 
	 * @param config
	 */
	public void stopServer(DeployConfig config, SpeedProgress speed) {
		boolean isWait=true;
		if (OSinfo.isWindows()) {
			isWait = false;
		}
		
        String stopStr = config.tomat_path.concat(config.tomcat_bin).concat(File.separator).concat(config.tomcat_stop_shell);
        String result=RuntimeUtil.execCmd(stopStr,isWait,true);
        if(result.contains("Could not contact")) {
        	speed.setInfo("停止服务发现未启动");
        }else {
        	  speed.setInfo(result);
        }
	}

	/**
	 * 启动服务
	 * 
	 * @param config
	 */
	public void startServer(DeployConfig config, SpeedProgress speed) {
		boolean isWait=true,isRead=true;
		if (OSinfo.isWindows()) {
			isRead=isWait = false;
		}
		String startStr = config.tomat_path.concat(config.tomcat_bin).concat(File.separator).concat(config.tomcat_start_shell);
		String result = RuntimeUtil.execCmd(startStr,isWait,isRead);
		speed.setInfo(result);
	}

	/**
	 * 清除缓存
	 * 
	 * @param config
	 */
	public void cleanCache(DeployConfig config, SpeedProgress speed) {
		try {
			String cleanPath = config.tomat_path+config.tomcat_cache_path;
			File cleanFile= new File(cleanPath);
			if (cleanFile.exists() && cleanFile.canWrite())
				FileUtils.forceDelete(cleanFile);
		} catch (Exception e) {
			throw new BusinessException("清除缓存失败");
		}

	}

	/**
	 * 拷贝到server
	 * 
	 * @param config
	 * @param sourcePath copy源
	 */
	public void copyWar(DeployConfig config, SpeedProgress speed,List<String>wars,String sourcePath ) {
		try {
			String destpath = config.tomat_path.concat(config.tomcat_war_path)+File.separator;
			if (sourcePath == null)
				sourcePath = config.soft_file_path + config.soft_unzip_name + File.separator + config.soft_war_name+ File.separator;
			for (String warName : wars) {
				if ("separate".equals(config.tomcat_model)) {
					destpath =config.tomat_path.concat(config.getWarStorageList().get(warName)).concat(File.separator);
				}
				speed.setInfo(String.format("正在copy%s",warName));
				String temp_path=destpath.concat(warName);
				File srcFile = new File(sourcePath.concat(warName));
				File destFile = new File(temp_path);
				FileUtils.copyFile(srcFile, destFile);
				speed.setInfo(String.format("%s拷贝到server完成",warName));
			}
		} catch (Exception e) {
			throw new BusinessException("copy到server失败");
		}
	}

	/**
	 * 安装服务
	 * 
	 * @param config
	 * @throws InterruptedException 
	 */
	public void install(DeployConfig config,SpeedProgress speed)  {
		try {
			// 进行解压
			speed.setInfo(0, "解压程序包", "正在解压程序包");
			this.unPackage(config, speed);

			List<String> wars = updateWars(config, speed);
			List<String> sqls = updateSqls(config, speed);
			// 检测程序包
			boolean over = inspectFile(wars, sqls, speed);

			if (over)
				return;

			// 停止命令
			speed.setInfo(10, "停止服务", "开始停止服务");
			this.stopServer(config, speed);
			CommonUtil.sleep(5000);
			// 如果有脚本文件-执行脚本
			if (!sqls.isEmpty()) {
				speed.setInfo(20, "执行脚本", "开始执行脚本");
				this.executeSql(config, speed);
			}

			// 如果有更新程序-
			if (!wars.isEmpty()) {
				// 备份war
				speed.setInfo(50, "备份war", "开始备份war");
				backupWar(config, wars, speed);
				// 清除（war和缓存）
				speed.setInfo(70, "清除缓存", "开始清除缓存");
				this.delOldWar(config, speed, wars);
				this.cleanCache(config, speed);
				// copy war到指定位置
				speed.setInfo(90, "copy 安装程序到指定位置", "copy 安装程序到指定位置");
				this.copyWar(config, speed, wars, null);
			}

			// 启动
			speed.setInfo(95, "重新启动服务", "开始启动服务");
			this.startServer(config, speed);
			// 清理临时文件区
			speed.setInfo(98, "清理临时文件区", "清理临时文件区");
			this.cleanTempFile(config, speed);
			speed.setInfo(99, "更新版本号", "更新版本号");
			this.updateVersion(config);
			speed.setInfo(100, "程序安装结束", "程序安装结束");
		} catch (BusinessException e) {
			speed.setInfo("错误终止", e.getMessage());
			speed.setInfo("错误终止");
			speed.setStatus(0);
		} catch (Exception e) {
			speed.setInfo("错误终止", "发生错误终止");
			speed.setInfo("错误终止");
			speed.setStatus(0);
		}
	}

	/**
	 * 更新版本
	 * @param config
	 */
	public void updateVersion(DeployConfig config) {
		try {
			File currentVersionFile = FileUtil.getFile("current-version.txt");
			File tmpVersionFile = FileUtil.getFile("tmp-version.txt");
			File oldVersionFile = FileUtil.getFile("old-version.txt");
			FileUtils.writeByteArrayToFile(oldVersionFile, FileUtils.readFileToByteArray(currentVersionFile));
			FileUtils.writeByteArrayToFile(currentVersionFile, FileUtils.readFileToByteArray(tmpVersionFile));
		} catch (Exception e) {
			throw new BusinessException("更新版本号出错");
		}
	}
	
	
	/**
	 * 获取升级包文件名称
	 * @param config
	 * @param speed
	 * @return
	 */
	public List<String> updateWars(DeployConfig config,SpeedProgress speed){
		File file = new File( config.soft_file_path + config.soft_unzip_name + File.separator + config.soft_war_name);
		if (!file.exists() || !file.canRead()) {
			throw new BusinessException("解压缩程序包错误");
		}
		File[] wars = file.listFiles();
		List<String> warNames = new ArrayList<>();
		if (wars != null && wars.length > 0) {
			Arrays.asList(wars).stream().forEach(t -> warNames.add(t.getName()));
		}
		return warNames;
	}
	
	
	/**
	 * 获取需要执行的脚本文件
	 * @param config
	 * @param speed
	 * @return
	 */
	public List<String> updateSqls(DeployConfig config,SpeedProgress speed){
		File file = new File( config.soft_file_path + config.soft_unzip_name + File.separator + config.soft_sql_name);
		if (!file.exists() || !file.canRead()) {
			speed.setInfo("解压缩程序包错误");
			throw new BusinessException("解压缩程序包错误");
		}
		File[] sqls = file.listFiles();
		List<String> sqlNames = new ArrayList<>();
		if (sqls != null && sqls.length > 0) {
			Arrays.asList(sqls).stream().forEach(t -> sqlNames.add(t.getName()));
		}
		return sqlNames;
	}
	
	/**
	 *   检测程序包
	 * @param wars
	 * @param sqls
	 * @param speed
	 * @return
	 */
	public boolean inspectFile(List<String> wars, List<String> sqls, SpeedProgress speed) {
		if (wars.isEmpty()) {
			speed.setInfo("无任何需要更新的程序包");
		}
		if (sqls.isEmpty()) {
			speed.setInfo("无任何执行的脚本");
		}
		if (wars.isEmpty() && sqls.isEmpty()) {
			speed.setInfo("无更新程序，更新结束");
			return true;
		}
		return false;
	}
	
	/**
	 * 清理临时文件
	 * @param config
	 * @param speed
	 */
	public void cleanTempFile(DeployConfig config,SpeedProgress speed) {
		try {
			File file = new File(config.soft_file_path + config.soft_unzip_name);
			if (file.exists() && file.canWrite())
				FileUtils.deleteDirectory(file);
		} catch (IOException e) {
			throw new BusinessException("清理临时文件失败");
		}
	}
	
	
	/**
	 *  删除旧包
	 * @param config
	 * @param speed
	 * @return
	 */
	public void delOldWar(DeployConfig config,SpeedProgress speed,List<String> wars){
		try {
			String cleanPath = config.tomat_path.concat(config.tomcat_war_path);
			for (String warName : wars) {
				String warPath=cleanPath + File.separator + warName;
				String warDirectoryPath=warPath.substring(0,warPath.lastIndexOf("."));
				File war= new File(warPath);
				File warDirectory= new File(warDirectoryPath);
				if (war.exists() && war.canWrite())
					FileUtils.forceDelete(war);
				if (warDirectory.exists() && warDirectory.canWrite())
					FileUtils.deleteDirectory(warDirectory);
			}
		} catch (Exception e) {
			throw new BusinessException("删除旧包出错");
		}

	
	}
	
	/**
	 * 清理备份文件
	 * @param config
	 * @param speed
	 * @num  保留最近的文件个数
	 */
	public SpeedProgress clearBackFile(DeployConfig config,Integer num) {
		SpeedProgress speed=new SpeedProgress(0, "清理备份文件");
		try {
			// 清理/data/deploy/sql
			File sqlDir = new File(config.backups_database_path);
			if (sqlDir.exists() && sqlDir.isDirectory()) {
				File[] list = sqlDir.listFiles();
				if (list != null && list.length > 0) {
					List<File> temps = Arrays.asList(list).stream().sorted((f1, f2) -> {
						long t = f1.lastModified() - f2.lastModified();
						return t > 0 ? -1 : 1;
					}).collect(Collectors.toList());
					int k = 0;
					for (File file : temps) {
						if (file.getName().endsWith(".sql")) {
							file.delete();
						} else {
							if (k < num)
								++k;
							else
								file.delete();
						}
					}
				}

			}
			File warDir = new File(config.backups_war_path);
			if (warDir.exists() && warDir.isDirectory()) {
				File[] list = warDir.listFiles();
				if (list != null && list.length > 0) {
					List<File> temps = Arrays.asList(list).stream().sorted((f1, f2) -> {
						long t = f1.lastModified() - f2.lastModified();
						return t > 0 ? -1 : 1;
					}).collect(Collectors.toList());
					int k = 0;
					for (File file : temps) {
						if (k < num)
							++k;
						else
							FileUtils.forceDelete(file);
					}
				}

			}
			speed.setInfo(100,"清理备份文件", "清理完成");
		} catch (Exception e) {
			speed.setInfo("清理备份文件出错");
		}
		return speed;
	}
	
	
	/**
	 * 从备份中恢复包
	 * @param config
	 */
	public void recoveryPackage(DeployConfig config,SpeedProgress speed,String fileName) {
		String backPath = config.backups_war_path.concat(fileName).concat(File.separator);
		File file = new File(backPath);
		if (!file.exists())
			throw new BusinessException("不存在的war");
		//停止命令
     	speed.setInfo(0, "停止服务","开始停止服务");
     	this.stopServer(config, speed);
     	CommonUtil.sleep(5000);
		//copy
		File[] wars = file.listFiles();
		List<String> warNames = new ArrayList<>();
		if (wars != null && wars.length > 0) {
			Arrays.asList(wars).stream().forEach(t -> warNames.add(t.getName()));
		}
		
     	//如果有更新程序-
     	if(!warNames.isEmpty()) {
     		//备份war
     		speed.setInfo(50, "备份war","开始备份war");
     		backupWar(config, warNames, speed);
    		//清除（war和缓存）
     		speed.setInfo(70, "清除缓存","开始清除缓存");
     		this.delOldWar(config, speed,warNames);
    		this.cleanCache(config, speed);
    		//copy war到指定位置
    		speed.setInfo(90, "copy 安装程序到指定位置","copy 安装程序到指定位置");
    		copyWar(config, speed, warNames, backPath);
     	}
    	//启动
     	speed.setInfo(95, "重新启动服务","开始启动服务");
		this.startServer(config, speed);
        //清理临时文件区
		speed.setInfo(98, "清理临时文件区","清理临时文件区");
		this.cleanTempFile(config, speed);
		speed.setInfo(100, "程序安装结束","程序安装结束");
	
	}
}
